#define REMOTE_CONTROL_HTML \
    "\
<!DOCTYPE html>\
<html lang=\"en\">\
<head>\
  <meta charset=\"UTF-8\" />\
  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\
  <title>Vertical Sliders with Joysticks</title>\
  <style>\
    body {\
      margin: 0;\
      height: 100vh;\
      background: #1e1e2f;\
      display: flex;\
      flex-direction: column;\
      justify-content: space-between;\
      align-items: center;\
    }\
    .row-container {\
      display: flex;\
      flex-direction: row; /* 水平排列 */\
      justify-content: center;\
      align-items: center;\
      margin: 10px 0; /* 减少拨杆和摇杆之间的距离 */\
    }\
    .slider-container {\
      display: flex;\
      flex-direction: column;\
      align-items: center;\
      margin: 0 40px; /* 增大拨杆之间的距离 */\
    }\
    .slider-container input[type=\"range\"] {\
      writing-mode: bt-lr; /* 竖直方向 */\
      -webkit-appearance: slider-vertical; /* 适配 Webkit 浏览器 */\
      width: 8px;\
      height: 150px; /* 调整拨杆长度 */\
      margin: 10px 0;\
    }\
    .slider-container label {\
      color: white;\
      font-size: 16px;\
    }\
    .joystick-container {\
      width: 200px;\
      height: 200px;\
      background: rgba(255, 255, 255, 0.1);\
      position: relative;\
      touch-action: none;\
      margin: 0 10px; /* 减少拨杆和摇杆之间的距离 */\
    }\
    .joystick {\
      width: 80px;\
      height: 80px;\
      background: #4caf50;\
      position: absolute;\
      top: 60px;\
      left: 60px;\
      transition: 0.1s ease-out;\
      touch-action: none;\
    }\
    .button-container {\
      display: flex;\
      justify-content: center;\
      margin: 20px 0;\
    }\
    .button-container button {\
      width: 150px;\
      height: 50px;\
      margin: 0 10px;\
      font-size: 16px;\
      background-color: #4caf50;\
      color: white;\
      border: none;\
      border-radius: 5px;\
      cursor: pointer;\
    }\
    .button-container button.stop {\
      background-color: #f44336;\
    }\
  </style>\
</head>\
<body>\
  <!-- 拨杆行 -->\
  <div class=\"row-container\">\
    <div class=\"slider-container\">\
      <label for=\"slider-1\">拨杆 1</label>\
      <input type=\"range\" id=\"slider-1\" min=\"-1\" max=\"1\" step=\"1\" value=\"0\" />\
      <span id=\"slider-1-value\">0</span>\
    </div>\
    <div class=\"slider-container\">\
      <label for=\"slider-2\">拨杆 2</label>\
      <input type=\"range\" id=\"slider-2\" min=\"-1\" max=\"1\" step=\"1\" value=\"0\" />\
      <span id=\"slider-2-value\">0</span>\
    </div>\
  </div>\
  <!-- 摇杆行 -->\
  <div class=\"row-container\">\
    <div class=\"joystick-container\" id=\"joystick-container-1\">\
      <div class=\"joystick\" id=\"joystick-1\"></div>\
    </div>\
    <div class=\"joystick-container\" id=\"joystick-container-2\">\
      <div class=\"joystick\" id=\"joystick-2\"></div>\
    </div>\
  </div>\
  <!-- 按钮行 -->\
  <div class=\"button-container\">\
    <button onclick=\"startCommunication()\">开启遥控器</button>\
    <button class=\"stop\" onclick=\"stopCommunication()\">关闭遥控器</button>\
  </div>\
  <script>\
    var xhttp = new XMLHttpRequest();\
    function controlSolve(code) {\
      xhttp.open('GET', './rc_solve?ctrl_code=' + code, true);\
      xhttp.send();\
    }\
    function startCommunication() {\
      controlSolve('id=button_on,value=on');\
      console.log('通信已开启');\
    }\
    function stopCommunication() {\
      controlSolve('id=button_off,value=off');\
      console.log('通信已关闭');\
    }\
    function createJoystick(containerId, joystickId) {\
      const container = document.getElementById(containerId);\
      const joystick = document.getElementById(joystickId);\
      const joystickSize = joystick.offsetWidth;\
      const center = {\
        x: container.offsetWidth / 2,\
        y: container.offsetHeight / 2,\
      };\
      function setJoystickPosition(x, y) {\
        joystick.style.left = `${x - joystickSize / 2}px`;\
        joystick.style.top = `${y - joystickSize / 2}px`;\
      }\
      function handleMove(clientX, clientY) {\
        const containerRect = container.getBoundingClientRect();\
        const dx = clientX - containerRect.left - center.x;\
        const dy = clientY - containerRect.top - center.y;\
        const clampedX = Math.max(-center.x + joystickSize / 2, Math.min(dx, center.x - joystickSize / 2));\
        const clampedY = Math.max(-center.y + joystickSize / 2, Math.min(dy, center.y - joystickSize / 2));\
        const x = center.x + clampedX;\
        const y = center.y + clampedY;\
        setJoystickPosition(x, y);\
        const normX = clampedX / (center.x - joystickSize / 2);\
        const normY = clampedY / (center.y - joystickSize / 2);\
        const now = performance.now();\
        if (!handleMove.lastCallTime || now - handleMove.lastCallTime > 30) {\
          controlSolve(`id=${joystickId},last=${handleMove.lastCallTime},now=${now},x=${normX.toFixed(2)},y=${normY.toFixed(2)}`);\
          handleMove.lastCallTime = now;\
        }\
      }\
      function resetJoystick() {\
        setJoystickPosition(center.x, center.y);\
        controlSolve(`id=${joystickId},x=0.00,y=0.00`);\
      }\
      let activeTouches = {};\
      container.addEventListener(\"touchstart\", (e) => {\
        const containerRect = container.getBoundingClientRect();\
        for (const touch of e.changedTouches) {\
          if (\
            touch.clientX >= containerRect.left &&\
            touch.clientX <= containerRect.right &&\
            touch.clientY >= containerRect.top &&\
            touch.clientY <= containerRect.bottom\
          ) {\
            activeTouches[touch.identifier] = true;\
            handleMove(touch.clientX, touch.clientY);\
          }\
        }\
      });\
      container.addEventListener(\"touchmove\", (e) => {\
        for (const touch of e.changedTouches) {\
          if (activeTouches[touch.identifier]) {\
            handleMove(touch.clientX, touch.clientY);\
          }\
        }\
      });\
      container.addEventListener(\"touchend\", (e) => {\
        for (const touch of e.changedTouches) {\
          if (activeTouches[touch.identifier]) {\
            delete activeTouches[touch.identifier];\
            resetJoystick();\
          }\
        }\
      });\
      container.addEventListener(\"touchcancel\", (e) => {\
        for (const touch of e.changedTouches) {\
          if (activeTouches[touch.identifier]) {\
            delete activeTouches[touch.identifier];\
            resetJoystick();\
          }\
        }\
      });\
      resetJoystick();\
    }\
    createJoystick(\"joystick-container-1\", \"joystick-1\");\
    createJoystick(\"joystick-container-2\", \"joystick-2\");\
    document.getElementById(\"slider-1\").addEventListener(\"input\", (e) => {\
      const value = e.target.value;\
      document.getElementById(\"slider-1-value\").innerText = value;\
      controlSolve(`id=slider1,value=${value}`);\
    });\
    document.getElementById(\"slider-2\").addEventListener(\"input\", (e) => {\
      const value = e.target.value;\
      document.getElementById(\"slider-2-value\").innerText = value;\
      controlSolve(`id=slider2,value=${value}`);\
    });\
  </script>\
</body>\
</html>\
"
// END =====================================================
